package org.firebug.common.tools.core;

import org.springframework.beans.BeanUtils;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * @Author: 张晓东
 */
public final class BeanTools extends BeanUtils {
    /**
     * 对象集合中 提取 属性集合
     * Collection<Bean> -> List<field>
     * 例：Collection<Bean> -> List<id>: collectField(users, t -> t.getId());
     */
    public static <B, F> List<F> collectField(Collection<B> beans, Function<B, F> field) {
        return beans.stream().map(b -> field.apply(b)).collect(Collectors.toList());
    }

    /**
     * 对象集合中 转 属性+对象的map
     * Collection<Bean> -> Map<field,Bean>
     * 例：Map<id, Bean>: toMap(beans, t -> t.getId())
     */
    public static <F, B> Map<F, B> toMap(Collection<B> beans, Function<B, F> field) {
        return beans.stream().collect(Collectors.toMap(field, b -> b, (k1, k2) -> k2));
    }

    /**
     * 对象属性复制(手动)
     * @param source: 原始对象
     * @param target: 目标对象
     * @param converter: 手动转换器, 如：(s, t) -> {t.setX(s.getY()})
     */
    public static <S, T> void copyProperties(S source, T target, BiConsumer<S, T> converter) {
        converter.accept(source, target);
    }

    /**
     * 对象集合 属性复制(自动) sources -> targets
     * @param sources: 原始对象集合
     * @param targets: 目标对象集合
     * @param targetKey： targets的外键, 如: t -> t.getX()
     */
    public static <S, T, K> void copyProperties(Map<K, S> sources, Collection<T> targets, Function<T, K> targetKey) {
        targets.stream().filter(t -> sources.containsKey(targetKey.apply(t))).forEach(t -> copyProperties(sources.get(targetKey.apply(t)), t));
    }

    /**
     * 对象集合 属性复制(手动) sources -> targets
     * @param sources: 原始对象集合
     * @param targets: 目标对象集合
     * @param targetKey： targets的外键, 如: t -> t.getX()
     * @param converter: 手动转换器, 如：(s, t) -> {t.setX(s.getY()})
     */
    public static <S, T, K> void copyProperties(Map<K, S> sources, Collection<T> targets, Function<T, K> targetKey, BiConsumer<S, T> converter) {
        targets.stream().filter(t -> sources.containsKey(targetKey.apply(t))).forEach(t -> copyProperties(sources.get(targetKey.apply(t)), t, converter));
    }

    /**
     * 对象集合 属性复制(自动) sources -> targets
     * @param sources: 原始对象集合
     * @param targets: 目标对象集合(not empty)
     * @param sourceKey： sources的主键, 如: s -> s.getX()
     * @param targetKey： targets的外键, 如: t -> t.getY()
     */
    public static <S, T, K> void copyProperties(Collection<S> sources, Collection<T> targets, Function<S, K> sourceKey, Function<T, K> targetKey) {
        copyProperties(toMap(sources, sourceKey), targets, targetKey);
    }

    /**
     * 对象集合 属性复制(手动) sources -> targets
     * @param sources: 原始对象集合
     * @param targets: 目标对象集合(not empty)
     * @param sourceKey： sources的主键, 如: s -> s.getX()
     * @param targetKey： targets的外键, 如: t -> t.getY()
     * @param converter: 手动转换器, 如：(s, t) -> {t.setX(s.getY()})
     */
    public static <S, T, K> void copyProperties(Collection<S> sources, Collection<T> targets, Function<S, K> sourceKey, Function<T, K> targetKey, BiConsumer<S, T> converter) {
        copyProperties(toMap(sources, sourceKey), targets, targetKey, converter);
    }

    /**
     * 对象集合 属性复制并填充emptyTargets(自动) sources -> emptyTargets
     * @param sources: 原始对象集合
     * @param emptyTargets：目标对象空集合(方法执行后填充)
     * @param supplier：目标对象new, 如：Source::new
     */
    public static <S, T> void copyProperties(Collection<S> sources, Collection<T> emptyTargets, Supplier<T> supplier) {
        sources.stream().forEach(source -> {
            T target = supplier.get();
            copyProperties(source, target);
            emptyTargets.add(target);
        });
    }
    /**
     * 对象集合 属性复制并填充emptyTargets(手动) sources -> emptyTargets
     * @param sources: 原始对象集合
     * @param emptyTargets：目标对象空集合(方法执行后填充)
     * @param supplier：目标对象new, 如：Source::new
     * @param converter: 手动转换器, 如：(s, t) -> {t.setX(s.getY()})
     */
    public static <S, T> void copyProperties(Collection<S> sources, Collection<T> emptyTargets, Supplier<T> supplier, BiConsumer<S, T> converter) {
        sources.stream().forEach(source -> {
            T target = supplier.get();
            copyProperties(source, target, converter);
            emptyTargets.add(target);
        });
    }
}